package com.xsomnus.rpc.provider.server;

import com.xsomnus.rpc.common.annotation.RPCService;
import com.xsomnus.rpc.common.codec.FastJsonDecoder;
import com.xsomnus.rpc.common.codec.FastJsonEncoder;
import com.xsomnus.rpc.provider.config.RPCServerProperties;
import com.xsomnus.rpc.provider.registry.ServiceRegistry;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * @author @xsomnus666_xiawenye★
 * @since 2019/4/22 0022 14:06
 * 当你发现自己的才华撑不起野心时，就请安静下来学习吧
 */
@Slf4j
@Component
public class NettyServer implements ApplicationContextAware, InitializingBean {

    private static final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    private static final EventLoopGroup workerGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() << 1);

    private Map<String, Object> serviceMap = new HashMap<>();

    @Autowired
    RPCServerProperties rpcServerProperties;

    @Autowired
    ServiceRegistry serviceRegistry;

    @Override
    public void afterPropertiesSet() throws Exception {
        log.debug("rpcServerProperties: {}", rpcServerProperties);
        start();
    }

    private void start() {
        final NettyServerHandler handler = new NettyServerHandler(serviceMap);
        new Thread(() -> {
            try {
                ServerBootstrap serverBootstrap = new ServerBootstrap();
                serverBootstrap.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_BACKLOG, 1024)
                        .childOption(ChannelOption.SO_KEEPALIVE, true)
                        .childOption(ChannelOption.TCP_NODELAY, true)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel socketChannel) throws Exception {
                                ChannelPipeline pipeline = socketChannel.pipeline();
                                pipeline.addLast(new IdleStateHandler(0, 0, 60));
                                pipeline.addLast(new FastJsonEncoder());
                                pipeline.addLast(new FastJsonDecoder());
                                pipeline.addLast(handler);
                            }
                        });
                ChannelFuture channelFuture = serverBootstrap.bind(rpcServerProperties.getHost(), rpcServerProperties.getPort()).sync();
                log.info("RPC服务启动, 对应的IP: {}, 端口: {}", rpcServerProperties.getHost(), rpcServerProperties.getPort());
                serviceRegistry.register(String.format("%s:%d", rpcServerProperties.getHost(), rpcServerProperties.getPort()));
                channelFuture.channel().closeFuture().sync();
            } catch (Exception e) {
                e.printStackTrace();
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }).start();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> rpcBeans = applicationContext.getBeansWithAnnotation(RPCService.class);
        for (Object bean : rpcBeans.values()) {
            Class<?> clazz = bean.getClass();
            Class<?>[] interfaces = clazz.getInterfaces();
            for (Class<?> inter : interfaces) {
                String interfaceName = inter.getName();
                String simpleName = inter.getSimpleName();
                log.debug("加载服务类: {}, simpleName : {}", interfaceName, simpleName);
                serviceMap.put(simpleName, bean);
            }
        }
        log.debug("已加载全部服务接口:{}", serviceMap);
    }
}
